#include "Effect.h"

#include <ezlogger_headers.hpp>

namespace Crows3D
{
   EffectPositionTextureNormal::EffectPositionTextureNormal(void)
   {
      EZLOGGERSTREAM << "EffectPositionTextureNormal Constructor" << std::endl;

      pVS          = NULL;         
      pPS          = NULL;         
      pLayout      = NULL;
      samplerState = NULL;
      mb           = NULL;
      lb           = NULL;
   }

   EffectPositionTextureNormal::~EffectPositionTextureNormal(void)
   {
      EZLOGGERSTREAM << "EffectPositionTextureNormal Destructor" << std::endl;

      if (mb)
      {
         mb->Release();
         mb = NULL;
      }

      if (lb)
      {
         lb->Release();
         lb = NULL;
      }

      if (samplerState)
      {
         samplerState->Release();
         samplerState = NULL;
      }

      if (pLayout)
      {
         pLayout->Release();
         pLayout = NULL;
      }

      if (pVS)
      {
         pVS->Release();
         pVS = NULL;
      }
      
      if (pPS)
      {
         pPS->Release();
         pPS = NULL;
      }
   }

   bool EffectPositionTextureNormal::Initialize(Device *dev, std::wstring vsFile, std::wstring psFile)
   {
      ID3D10Blob* errorMessage;
      
      EZLOGGERSTREAM << "Initializing EffectPositionTextureNormal from files. "
         /*TODO fix<< "Vertex Shader: " << vsFile << ", Pixel Shader: " << psFile*/ << std::endl;

      //Create our shaders
      ID3D10Blob *VS, *PS;
      if ((FAILED(D3DX11CompileFromFile(vsFile.c_str(), 0, 0, "VShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 0, &VS, &errorMessage, 0))) ||
          (FAILED(D3DX11CompileFromFile(psFile.c_str(), 0, 0, "PShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 0, &PS, &errorMessage, 0))))
      {
         EZLOGGERSTREAM << "Failed To compile shader files" << std::endl;

         char* compileErrors;
         compileErrors = (char*)(errorMessage->GetBufferPointer());

         return false;
      }

      if (errorMessage != NULL)
      {
         EZLOGGERSTREAM << "Failed To compile shader files" << std::endl;

         char* compileErrors;
         compileErrors = (char*)(errorMessage->GetBufferPointer());
      }

      // encapsulate both shaders into shader objects
      if ((FAILED(dev->GetDevice()->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS))) ||
          (FAILED(dev->GetDevice()->CreatePixelShader( PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS))))
      {
         EZLOGGERSTREAM << "Failed To create shader objects" << std::endl;

         return false;
      }


      // create the input layout object
      D3D11_INPUT_ELEMENT_DESC ied[3] =
      {
         {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
         {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,       0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
         {"NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
      };

      LPVOID tmp  = VS->GetBufferPointer();
      UINT   tmp2 = VS->GetBufferSize();

      //HRESULT hr = dev->GetDevice()->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
      if (FAILED(dev->GetDevice()->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout)))
      { 
         EZLOGGERSTREAM << "Failed To create Input layout" << std::endl;

         return false;
      }

      VS->Release(); VS = NULL;
      PS->Release(); PS = NULL;


      // Create the sampler state
      D3D11_SAMPLER_DESC sampDesc;
      memset(&sampDesc, 0, sizeof(sampDesc));

      sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
      sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
      sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
      sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
      sampDesc.MipLODBias = 0.0f;
      sampDesc.MaxAnisotropy = 1;
      sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
      sampDesc.BorderColor[0] = 0;
	   sampDesc.BorderColor[1] = 0;
	   sampDesc.BorderColor[2] = 0;
	   sampDesc.BorderColor[3] = 0;
      sampDesc.MinLOD = 0;
      sampDesc.MaxLOD = D3D11_FLOAT32_MAX;

      if (FAILED(dev->GetDevice()->CreateSamplerState( &sampDesc, &samplerState)))
      {
         EZLOGGERSTREAM << "Failed To create sampler state" << std::endl;

         return false;
      }


      D3D11_BUFFER_DESC matrixBufferDesc;
      D3D11_BUFFER_DESC lightBufferDesc;


      // Set up constant buffer for matrix updates to vertex shader
      memset(&matrixBufferDesc, 0, sizeof(D3D11_BUFFER_DESC));
      matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;                    // write access access by CPU and GPU
      matrixBufferDesc.ByteWidth = sizeof(MatrixBuffer);               // size
      matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;         // use as a index buffer
      matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;        // allow CPU to write in buffer
      matrixBufferDesc.MiscFlags = 0;
	   matrixBufferDesc.StructureByteStride = 0;

      if (FAILED(dev->GetDevice()->CreateBuffer(&matrixBufferDesc, NULL, &mb))) // create the buffer
      {
         EZLOGGERSTREAM << "Failed To create constant buffer for matrix updates" << std::endl;

         return false;
      }

      // Set up constant buffer for light updates to vertex shader
      memset(&lightBufferDesc, 0, sizeof(D3D11_BUFFER_DESC));
      lightBufferDesc.Usage = D3D11_USAGE_DYNAMIC;                    // write access access by CPU and GPU
      lightBufferDesc.ByteWidth = sizeof(LightBuffer);               // size is the VERTEX struct * 3
      lightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;         // use as a index buffer
      lightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;        // allow CPU to write in buffer
      lightBufferDesc.MiscFlags = 0;
	   lightBufferDesc.StructureByteStride = 0;

      if (FAILED(dev->GetDevice()->CreateBuffer(&lightBufferDesc, NULL, &lb))) // create the buffer
      {
         EZLOGGERSTREAM << "Failed To create constant buffer for light data" << std::endl;

         return false;
      }
      






      return true;
   }


   bool EffectPositionTextureNormal::LoadDefaults(Device *dev)
   {
      return Initialize(dev, L".\\Resources\\Shaders\\PositionTexturedNormal.vs", L".\\Resources\\Shaders\\PositionTexturedNormal.ps");
   }

   void EffectPositionTextureNormal::Commit(Device * dev, Texture * texture, D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX proj)
   {
      // Set up our matrices
      D3D11_MAPPED_SUBRESOURCE mappedResource;
      

      if (FAILED(dev->GetDeviceContext()->Map(mb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
      {
         return;
      }
      else
      {
         // Load up our constant buffer
         MatrixBuffer *matrixData = (MatrixBuffer*)mappedResource.pData;

         matrixData->world = world;
         matrixData->view  = view;
         matrixData->proj  = proj;

         D3DXMatrixTranspose(&matrixData->world, &matrixData->world);
         D3DXMatrixTranspose(&matrixData->view, &matrixData->view);
         D3DXMatrixTranspose(&matrixData->proj, &matrixData->proj);

         dev->GetDeviceContext()->Unmap(mb, 0);
      }

      if (FAILED(dev->GetDeviceContext()->Map(lb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
      {
         return;
      }
      else
      {
         // Load up our constant buffer
         LightBuffer *lightData = (LightBuffer*)mappedResource.pData;

         lightData->diffuseColor     = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
         lightData->diffuseDirection = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
         lightData->ambientColor     = D3DXVECTOR4(0.3f, 0.3f, 0.3f, 1.0f);

         dev->GetDeviceContext()->Unmap(lb, 0);
      }


      // Set the matrix buffer
      dev->GetDeviceContext()->VSSetConstantBuffers(0, 1, &mb);
      dev->GetDeviceContext()->PSSetConstantBuffers(0, 1, &lb);


      // Set our texture
      ID3D11ShaderResourceView* t = texture->GetTexture();
      dev->GetDeviceContext()->PSSetShaderResources(0, 1, &t);
      dev->GetDeviceContext()->PSSetSamplers(0, 1, &samplerState);
      
      dev->GetDeviceContext()->IASetInputLayout(pLayout);

      // Apply our shaders
      dev->GetDeviceContext()->VSSetShader(pVS, 0, 0);
      dev->GetDeviceContext()->PSSetShader(pPS, 0, 0);
   }























   
   
   
   EffectPosition2DTexture::EffectPosition2DTexture(void)
   {
      EZLOGGERSTREAM << "EffectPosition2DTexture Constructor" << std::endl;

      pVS          = NULL;         
      pPS          = NULL;         
      pLayout      = NULL;
      samplerState = NULL;
      mb           = NULL;
   }

   EffectPosition2DTexture::~EffectPosition2DTexture(void)
   {
      EZLOGGERSTREAM << "EffectPosition2DTexture Destructor" << std::endl;

      if (mb)
      {
         mb->Release();
         mb = NULL;
      }

      if (samplerState)
      {
         samplerState->Release();
         samplerState = NULL;
      }

      if (pLayout)
      {
         pLayout->Release();
         pLayout = NULL;
      }

      if (pVS)
      {
         pVS->Release();
         pVS = NULL;
      }
      
      if (pPS)
      {
         pPS->Release();
         pPS = NULL;
      }
   }

   bool EffectPosition2DTexture::Initialize(Device *dev, std::wstring vsFile, std::wstring psFile)
   {
      ID3D10Blob* errorMessage;
      
      EZLOGGERSTREAM << "Initializing EffectPosition2DTexture from files. "
         /*TODO fix<< "Vertex Shader: " << vsFile << ", Pixel Shader: " << psFile*/ << std::endl;

      //Create our shaders
      ID3D10Blob *VS, *PS;
      if ((FAILED(D3DX11CompileFromFile(vsFile.c_str(), 0, 0, "VShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 0, &VS, &errorMessage, 0))) ||
          (FAILED(D3DX11CompileFromFile(psFile.c_str(), 0, 0, "PShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 0, &PS, &errorMessage, 0))))
      {
         EZLOGGERSTREAM << "Failed To compile shader files" << std::endl;


         char* compileErrors;
         compileErrors = (char*)(errorMessage->GetBufferPointer());

         return false;
      }

      if (errorMessage != NULL)
      {
         EZLOGGERSTREAM << "Failed To compile shader files" << std::endl;

         char* compileErrors;
         compileErrors = (char*)(errorMessage->GetBufferPointer());

         return false;
      }

      // encapsulate both shaders into shader objects
      if ((FAILED(dev->GetDevice()->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS))) ||
          (FAILED(dev->GetDevice()->CreatePixelShader( PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS))))
      {
         EZLOGGERSTREAM << "Failed To create shader objects" << std::endl;

         return false;
      }


      // create the input layout object
      D3D11_INPUT_ELEMENT_DESC ied[2] =
      {
         {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
         {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,       0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
      };

      LPVOID tmp  = VS->GetBufferPointer();
      UINT   tmp2 = VS->GetBufferSize();

      //HRESULT hr = dev->GetDevice()->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
      if (FAILED(dev->GetDevice()->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout)))
      { 
         EZLOGGERSTREAM << "Failed To create input layout" << std::endl;

         return false;
      }

      VS->Release(); VS = NULL;
      PS->Release(); PS = NULL;


      // Create the sampler state
      D3D11_SAMPLER_DESC sampDesc;
      memset(&sampDesc, 0, sizeof(sampDesc));

      sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
      sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
      sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
      sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
      sampDesc.MipLODBias = 0.0f;
      sampDesc.MaxAnisotropy = 1;
      sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
      sampDesc.BorderColor[0] = 0;
	   sampDesc.BorderColor[1] = 0;
	   sampDesc.BorderColor[2] = 0;
	   sampDesc.BorderColor[3] = 0;
      sampDesc.MinLOD = 0;
      sampDesc.MaxLOD = D3D11_FLOAT32_MAX;

      if (FAILED(dev->GetDevice()->CreateSamplerState( &sampDesc, &samplerState)))
      {
         EZLOGGERSTREAM << "Failed To create sampler state" << std::endl;

         return false;
      }


      D3D11_BUFFER_DESC matrixBufferDesc;


      // Set up constant buffer for matrix updates to vertex shader
      memset(&matrixBufferDesc, 0, sizeof(D3D11_BUFFER_DESC));
      matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;                    // write access access by CPU and GPU
      matrixBufferDesc.ByteWidth = sizeof(MatrixBuffer);               // size
      matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;         // use as a index buffer
      matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;        // allow CPU to write in buffer
      matrixBufferDesc.MiscFlags = 0;
	   matrixBufferDesc.StructureByteStride = 0;

      if (FAILED(dev->GetDevice()->CreateBuffer(&matrixBufferDesc, NULL, &mb))) // create the buffer
      {
         EZLOGGERSTREAM << "Failed To create constant buffer for matrix updates" << std::endl;
         return false;
      }

      return true;
   }
   
   bool EffectPosition2DTexture::LoadDefaults(Device *dev)
   {


      return true;
   }

   void EffectPosition2DTexture::Commit(Device * dev, Texture * texture, D3DXMATRIX world, D3DXMATRIX view, D3DXMATRIX proj)
   {
            // Set up our matrices
      D3D11_MAPPED_SUBRESOURCE mappedResource;
      

      if (FAILED(dev->GetDeviceContext()->Map(mb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
      {
         return;
      }
      else
      {
         // Load up our constant buffer
         MatrixBuffer *matrixData = (MatrixBuffer*)mappedResource.pData;

         matrixData->world = world;
         matrixData->view  = view;
         matrixData->proj  = proj;

         D3DXMatrixTranspose(&matrixData->world, &matrixData->world);
         D3DXMatrixTranspose(&matrixData->view, &matrixData->view);
         D3DXMatrixTranspose(&matrixData->proj, &matrixData->proj);

         dev->GetDeviceContext()->Unmap(mb, 0);
      }

      // Set the matrix buffer
      dev->GetDeviceContext()->VSSetConstantBuffers(0, 1, &mb);



      // Set our texture
      ID3D11ShaderResourceView* t = texture->GetTexture();
      dev->GetDeviceContext()->PSSetShaderResources(0, 1, &t);

      
      dev->GetDeviceContext()->IASetInputLayout(pLayout);

      // Apply our shaders
      dev->GetDeviceContext()->VSSetShader(pVS, NULL, 0);
      dev->GetDeviceContext()->PSSetShader(pPS, NULL, 0);

      dev->GetDeviceContext()->PSSetSamplers(0, 1, &samplerState);
      
   }



}